package com.jmodusoperandi.utils.model.schema;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstPool;
import javax.persistence.Column;

import org.apache.log4j.Logger;
import org.hibernate.cfg.AnnotationConfiguration;



/**
 * Schema creation
 * TODO: generate a reflection-utils project
 * 
 * @author Licinio Fdez. Maurelo (licinio.fernandez@gmail.com)
 * 
 */
public class SchemaCreationSample {

	public static Logger log = Logger.getLogger(SchemaCreationSample.class); 
	
	private static String[] modelPackages = {"com.dixired.backoffice.model.cms.dto"};
	
	private static String[] schemas = {"SCHEMANAME1" , "SCHEMANAME2"};
	
	private static boolean hsqldbSchemasCreated = false;

	public static void main(String[] args) throws Exception{		
		
		createHSQLDBSchemas();
		
		dropSchemas();
		
	}
	
	public static void createHSQLDBSchemas() throws Exception {
		if (hsqldbSchemasCreated){
			return;			
		}		
			log.info("creating schemas");

			Class.forName("org.hsqldb.jdbcDriver").newInstance();
			Connection conn = DriverManager.getConnection("jdbc:hsqldb:mem:test","sa", "");
			Statement stmt = conn.createStatement();
			String sqlBaseStatement = "CREATE SCHEMA XXX AUTHORIZATION DBA", sql;

			for (String schemaName : schemas) {
				sql = new String(sqlBaseStatement).replaceAll("XXX", schemaName);
				log.info("Attempting to execute : " + sql);
				stmt.execute(sql);
			}
			stmt.close();
			conn.close();
	
		hsqldbSchemasCreated = true;
	}
	
	public static void dropSchemas() throws Exception{
		//Existen varias formas de configurar el tema
		AnnotationConfiguration cfg = new AnnotationConfiguration();
	
		//Añadir todas las clases 
		
		for (String modelPackage : modelPackages){
			List<Class<?>> clazzes = getClasses( modelPackage );
			for( Class<?> clazz : clazzes){
				cfg.addAnnotatedClass(clazz);
			}			
		}		
		//Configuración del datasource
		//esto para el caso en el que queramos utilizar el pool de hibernate. No apto entorno PROD
		cfg	.setProperty("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver")
			.setProperty("hibernate.connection.url", "jdbc:hsqldb:mem:test")
			.setProperty("hibernate.connection.username", "sa")
			.setProperty("hibernate.connection.password", "")
			.setProperty("hibernate.connection.pool_size", "5");
		
		//hibernate dialect
		cfg	.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
		
		//generación del schema
		cfg	.setProperty("hibernate.hbm2ddl.auto", "create-drop");
		
		//ver sql generado en el camino hibernate.show_sql
		cfg	.setProperty("hibernate.show_sql", "true");
		
		//action
		cfg.buildSessionFactory().openSession();		
	}
	
	/**
	 * El atributo columnDefinition fastidia todo el tema
	 * TODO: Ñapa para eliminar 
	 * @param clazz
	 * @throws IOException
	 */
	public static void removeSchemaIncompatibilities(Class<?> clazz) throws IOException{
		//@Column(name="cli_clipping_id", updatable=false, columnDefinition = "varchar(" + ModelUtils.ID_LENGTH + ") COLLATE utf8_bin")
		//http://stackoverflow.com/questions/1635108/adding-java-annotations-at-runtime
		//http://www.csg.is.titech.ac.jp/~chiba/javassist/html/javassist/bytecode/AnnotationsAttribute.html

		Method[] methods = clazz.getMethods();
			Column a;
		for (Method method : methods){
			a = method.getAnnotation(Column.class);
			if ( a != null){
				
				//es un método anotado con column
				//hacking con javaassistant
				BufferedInputStream fin = new BufferedInputStream(new FileInputStream(clazz.getCanonicalName()));
				ClassFile cf = new ClassFile(new DataInputStream(fin));
				ConstPool cp = cf.getConstPool();
				AnnotationsAttribute attr = new AnnotationsAttribute(cp, AnnotationsAttribute.visibleTag);
				
								
			}
		}
		
	}
	
	/**
	 * Utility method used to fetch Class list based on a package name.
	 * 
	 * @param packageName
	 *            (should be the package containing your annotated beans.
	 */
	public static List<Class<?>> getClasses(String packageName) throws Exception {
		List<Class<?>> classes = new ArrayList<Class<?>>();
		File directory = null;
		try {
			ClassLoader cld = Thread.currentThread().getContextClassLoader();
			if (cld == null) {
				throw new ClassNotFoundException("Can't get class loader.");
			}
			String path = packageName.replace('.', '/');
			URL resource = cld.getResource(path);
			if (resource == null) {
				throw new ClassNotFoundException("No resource for " + path);
			}
			directory = new File(resource.getFile());
		} catch (NullPointerException x) {
			throw new ClassNotFoundException(packageName + " (" + directory
					+ ") does not appear to be a valid package");
		}
		if (directory.exists()) {
			String[] files = directory.list();
			for (int i = 0; i < files.length; i++) {
				if (files[i].endsWith(".class")) {
					// removes the .class extension
					classes.add(Class.forName(packageName + '.'
							+ files[i].substring(0, files[i].length() - 6)));
				}
			}
		} else {
			throw new ClassNotFoundException(packageName
					+ " is not a valid package");
		}

		return classes;
	}

	
}
